package cn.com.bluemoon.daps.domp.api;


import cn.com.bluemoon.daps.common.annotation.FcApiFor;
import cn.com.bluemoon.daps.common.constant.ServiceNameConstants;
import cn.com.bluemoon.daps.common.domain.FeignApiRespBean;
import cn.com.bluemoon.daps.common.domain.ResultBean;
import cn.com.bluemoon.daps.common.exception.DapException;
import cn.com.bluemoon.daps.common.exception.DapThrowException;
import cn.com.bluemoon.daps.common.toolkit.AesEncryptUtils;
import cn.com.bluemoon.daps.common.toolkit.BmAssetUtils;
import cn.com.bluemoon.daps.common.toolkit.BmBeanUtils;
import cn.com.bluemoon.daps.domp.api.param.RoleDto;
import cn.com.bluemoon.daps.domp.api.param.RoleParamPacker;
import cn.com.bluemoon.daps.domp.api.param.UserDto;
import cn.com.bluemoon.daps.domp.api.param.UserParamPacker;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.google.common.collect.ImmutableMap;
import io.swagger.annotations.Api;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;

@FeignClient(value = ServiceNameConstants.DapsApiDomp.DOMP_SERVICE)
@RequestMapping
@Api(tags = "FeignClient服务-蓝数通bd-domp-service")
public interface IDompService {
    Logger log = LoggerFactory.getLogger(IDompService.class);

    /**
     * @param username       username
     * @param passwrod       passwrod加密后的
     * @param isCookieLogin  Y
     * @param validationCode vyts
     * @param brand          999
     */
    @PostMapping("/user/login")
    public FeignApiRespBean<List<Map<String, Object>>> dompLogin(@RequestParam("username") Integer username,
                                                                 @RequestParam("password") String passwrod,
                                                                 @RequestParam(value = "isCookieLogin") String isCookieLogin,
                                                                 @RequestParam(value = "validationCode") String validationCode,
                                                                 @RequestParam(value = "brand") Integer brand);

    /**
     * 若测试环境和生产都可以登录，则走{@link IDompService#dompLogin(Integer, String)} 蓝数通那边统一进行接口适配
     *
     * @param username       username
     * @param passwrod       passwrod加密后的
     * @param isCookieLogin  Y
     * @param validationCode vyts
     * @param brand          999
     */
    @FcApiFor(team = {"数据资产团队"})
    default FeignApiRespBean<List<Map<String, Object>>> dompLogin(@RequestParam("username") Integer username,
                                                                  @RequestParam("password") String password) {
        try {
            return dompLogin(username, AesEncryptUtils.encrypt(password), "Y", "vyts", 999);
        } catch (Exception e) {
            throw new DapThrowException("密码加密失败！", e);
        }
    }

    /**
     * 获取用角色菜单信息
     */
    @PostMapping(value = "/domp/getData/{id}", produces = "application/json; charset=utf-8")
    public FeignApiRespBean<List<Map<String, Object>>> getUserRoleMenuApi(String jsonParam, @PathVariable("id") String id);

    /**
     * 获取用户菜单信息
     *
     * @param token  token
     * @param userId 用户id
     * @return 菜单数据
     */
    default Map<String, List<MenuDto>> getMenuPathByTokenAndUserId(String token, String userId) {
        BmAssetUtils.notNull(token, "token不可为空");
        BmAssetUtils.notNull(userId, "账号不可为空");
        Map<String, Object> reqMap = ImmutableMap.of("token", token, "method", "m006_4", "userId", userId);
        long start = System.currentTimeMillis();
        log.info("开始远程调用蓝数通菜单信息接口....");
        FeignApiRespBean<List<Map<String, Object>>> feignApiRespBean = getUserRoleMenuApi(JSON.toJSONString(reqMap), "");
        log.info("完成远程调用蓝数通菜单信息接口....,cost:{}ms", (System.currentTimeMillis() - start));
        log.debug("获取用户数据情况, req:{}, resp:{}", reqMap, feignApiRespBean.isOk());
        if (feignApiRespBean.isOk() && feignApiRespBean.getRespData() != null) {
            Map<String, List<MenuDto>> menuPathMap = feignApiRespBean.getRespData().stream().map(o -> {
                MenuDto menuDto = BmBeanUtils.mapToBean(o, MenuDto.class);
                return menuDto;
            }).filter(menuDto -> StringUtils.isNotBlank(menuDto.getMenu_path()))
                    .filter(menuDto -> !menuDto.getMenu_path().startsWith("http"))
                    .collect(Collectors.groupingBy(MenuDto::getMenu_path));
            return menuPathMap;
        }
        return Collections.emptyMap();
    }

    @PostMapping(value = "/domp/getData", produces = "application/json; charset=utf-8")
    public String dompCommonApi(String jsonParam);

    @PostMapping(value = "/user/getMenuPermission", produces = "application/json; charset=utf-8")
    public String getMenuPermission(String jsonParam);

    /**
     * 模糊获取获取用户列表
     * userName为空获取为空，而不是所有用户信息
     *
     * @param token  认证
     * @param userId 用户id
     *               pageSize 页数 默认100页
     * @return 匹配用户结果
     */
    default ResultBean<List<UserDto>> matchUserInfosByUserId(String token, String userId) {
        return matchUserInfos(token, null, userId);
    }

    /**
     * 模糊获取获取用户列表
     * userName为空获取为空，而不是所有用户信息
     *
     * @param token    认证
     * @param userName 用户 中文名
     * @param userId   用户id 可为空
     *                 pageSize 页数 默认100页
     * @return 匹配用户结果
     */
    default ResultBean<List<UserDto>> matchUserInfos(String token,
                                                     String userName,
                                                     String userId) {
        if (StringUtils.isBlank(userName) && StringUtils.isBlank(userId)) {
            // 不允许全量拉取pageSize数据
            return ResultBean.ok(Collections.emptyList());
        }
        final int pageSize = Integer.getInteger("domp.match.user.pageSize", 100);
        UserParamPacker packer = new UserParamPacker(token, userId, userName, pageSize);
        Map<String, Object> req = packer.toMap("m005_2");
        String resp = dompCommonApi(JSON.toJSONString(req));
        @SuppressWarnings("unchecked") FeignApiRespBean<JSONArray> respBean = JSON.parseObject(resp, FeignApiRespBean.class);
        log.debug("获取用户数据情况, req:{}, resp:{}", req, resp);
        if (respBean.isOk() && respBean.getRespData() != null) {
            JSONArray json = respBean.getRespData();
            List<UserDto> userDtos = UserDto.fromObjs(json);
            return ResultBean.ok(userDtos);
        }
        return ResultBean.ok(Collections.emptyList());
    }

    /**
     * 获取用户角色
     * <p>
     * 用户信息：登录token，用户工号
     * token和工号没强制要求同一人
     * token作为认证
     * userAccount作为获取数据，返回该用户的橘色信息
     * </p>
     *
     * @param userId 工号
     * @param token  认证
     * @return 角色信息
     */
    @SuppressWarnings("unchecked")
    @FcApiFor(team = {"IT中心/技术研发部/供应链研发团队/SCM开发组"})
    default ResultBean<List<RoleDto>> getUserRoles(String token, String userId) {
        RoleParamPacker paramPacker = new RoleParamPacker(token, userId);
        Map<String, Object> req = paramPacker.toMap("m005_8");
        String resp = this.dompCommonApi(JSON.toJSONString(req));
        FeignApiRespBean<JSONArray> respBean = JSON.parseObject(resp, FeignApiRespBean.class);
        log.debug("获取用户数据情况, req:{}, resp:{}", req, resp);
        if (respBean.isOk() && respBean.getRespData() != null) {
            JSONArray json = respBean.getRespData();
            List<UserDto> userDtos = UserDto.fromObjs(json);
            List<RoleDto> roleDtos = userDtos.stream().flatMap(u -> u.getRoles().stream())
                    .filter(d -> Boolean.TRUE.equals(d.getSelected()))
                    .collect(Collectors.toList());
            return ResultBean.ok(roleDtos);
        }
        return ResultBean.ok(Collections.emptyList());
    }

    /**
     * 通过该UserId和token判断用户是否是“roleNames集合中的一个”
     *
     * @param roleNames 期望有哪些角色（其中即可为true）
     */
    default boolean hasSysRoleByAuthInfos(@NotNull String token, @NotNull String userId, Set<String> roleNames) {
        if (CollectionUtil.isEmpty(roleNames)) return false;
        return matchSysRoleByAuthInfo(token, userId, roleDto -> roleNames.contains(roleDto.getRoleName()));
    }

    /**
     * 判断该用户userId是否有指定角色predicate
     */
    default boolean matchSysRoleByAuthInfo(@NotNull String token, @NotNull String userId, Predicate<RoleDto> roleMatch) {
        BmAssetUtils.isFalse(StringUtils.isBlank(token), () -> new DapException("认证信息不可为空"));
        BmAssetUtils.isFalse(StringUtils.isBlank(userId), () -> new DapException("用户id不可为空"));
        List<RoleDto> roleDtoList = null;
        try {
            cn.com.bluemoon.daps.common.domain.ResultBean<List<RoleDto>> resultBean = getUserRoles(token, userId);
            roleDtoList = resultBean.getContent();
        } catch (Exception e) {
            log.error("远程调用异常[iDompService#getUserRoles]", e);
            return false;
        }
        if (CollectionUtil.isEmpty(roleDtoList)) {
            log.debug("用户ID{}获取角色为空", userId);
            return false;
        }
        boolean match = roleDtoList.stream().anyMatch(roleMatch);
        log.debug("用户id{}获取角色情况{}", userId, match);
        return match;
    }

    @Data
    public class MenuDto implements Serializable {
        private static final long serialVersionUID = 1L;
        private Integer menu_sort;
        private String menu_control;
        private String menu_icon_activated;
        private String menu_icon_unactivated;
        private Integer mid;
        private String menu_app_type;
        private Integer menu_parent_id;
        private String menu_name;
        private String menu_path;
        private String menu_level;
        private String menu_des;
        private String has_child;
        private String status;
    }
}
